﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ToWaDBFirst.Models
{

    /**
     * Service class containing service methods.
     **/
    public class ToWaService
    {
        
        /**
         * Class object to manage database operations.
         **/
        ToWaDBFirstDBEntities2 db = new ToWaDBFirstDBEntities2();

        /**
         * Method to add a new row in the lastvisit table in the database to keep track of how many times the user
         * has logged in.
         **/
        public void AddVisit(string username)
        {
            var id = db.users.Single(u => u.username == username).id;
            db.lastvisits.Add(new lastvisit { userid = id, visit = System.DateTime.Now });
            db.SaveChanges();
        }

        /**
         * Method to find a specified message between two dates, from who it's sent and to who it's sent.
         * If there are invalid dates, todays date and tomorrows date will be used in the query. If the fromWho field
         * is empty all messages between the two dates will be fetched.
         * Returns a list of messages found between the specified dates.
         **/
        public List<message> FindMessage(string fromDate, string toDate, string fromWho, string username)
        {
            List<message> list = new List<message>();
            if (!string.IsNullOrEmpty(fromDate))
            {
                fromDate = fromDate.Replace('/', '-');
            }
            if (!string.IsNullOrEmpty(toDate))
            {
                toDate = toDate.Replace('/', '-');
            }
            DateTime from;
            DateTime to;
            try
            {
                from = DateTime.Parse(fromDate);
            }
            catch
            {
                from = DateTime.Today;
            }
            try
            {
                to = DateTime.Parse(toDate);
            }
            catch
            {
                to = DateTime.Today.AddDays(1);
            }

            if (DateTime.Compare(from, to) > 0)
            {
                DateTime temp = from;
                from = to;
                to = temp;
            }

            var userId = db.users.Single(u => u.username == username).id;
            int fromUserId = -1;
            List<message> messages;
            try
            {
                fromUserId = db.users.Single(p => p.username == fromWho).id;
                messages = db.messages.Where(d => d.posted >= from).Where(d => d.posted <= to).Where(d => d.fromUser == fromUserId).ToList();
            }
            catch
            {
                messages = db.messages.Where(d => d.posted >= from).Where(d => d.posted <= to).ToList();
            }
                
            foreach (var m in messages)
            {
                try
                {
                    db.messagetoes.Where(c => c.messageid == m.id).Single(c => c.userid == userId);
                    list.Add(m);
                }
                catch { }
            }
            
            return list;
        }

        /**
         * Method to get all groups available and check if the user is in the group.
         * Returns a list containing all groups the specified user is in.
         **/
        public List<GroupWrapper> GetGroups(string username)
        {
            List<GroupWrapper> wrapper = new List<GroupWrapper>();
            int id = db.users.Single(u => u.username == username).id;
            var groups = db.groups.Include("usersingroups");
            foreach (var g in groups)
            {
                GroupWrapper gw = new GroupWrapper();
                gw.groupId = g.id;
                gw.name = g.name;
                gw.members = g.usersingroups.Count;
                bool member = true;
                try
                {
                    g.usersingroups.Single(u => u.userid == id);
                }
                catch
                {
                    member = false;
                }
                gw.member = member;
                wrapper.Add(gw);
            }

            return wrapper;
        }

        /**
         * Method for a user to join a group with a specified groupId and username. The users in group table is
         * used to get the users with the groupId and username. If the result is 0 the user is added to the group,
         * and then return true. Otherwise false.
         **/
        public bool JoinGroup(int groupId, string username)
        {
            var uId = db.users.Single(u => u.username == username).id;
            var query = from g in db.usersingroups
                        where g.groupid == groupId &&
                            g.userid == uId
                        select g;
            if (query.Count() == 0)
            {
                db.usersingroups.Add(new usersingroup { groupid = groupId, userid = uId });
                db.SaveChanges();
                return true;
            }
            return false;
        }

        /**
         * Method for a user to leave a specified group using a groupId and a username. The method gets the userId
         * with the username from the database and then searches for the group with the specified groupId to remove
         * the current user from the group. If the remove was successful true is returned, otherwise false.
         **/
        public bool LeaveGroup(int groupId, string username)
        {
            var uId = db.users.Single(u => u.username == username).id;

            try
            {
                var test = db.usersingroups.Where(g => g.groupid == groupId).Single(u => u.userid == uId);
                db.usersingroups.Remove(test);
                db.SaveChanges();
                return true;
            }
            catch
            {
                return false;
            }
        }

        /**
         * Method to return the number of logins the specified user has logged in during the specified number of days
         * in the past. Returns te number of logins the user has done the past days.
         **/
        public int NumberOfLoginsInDays(int nrOfDays, string username)
        {
            DateTime thirtyDaysAgo = System.DateTime.Now.AddDays(-nrOfDays);
            var id = db.users.Single(u => u.username == username).id;
            int logins = db.lastvisits.Where(l => l.userid == id).Where(l => l.visit > thirtyDaysAgo).Count();

            return logins;
        }

        /**
         * Method to get the latest date the user logged in, previous to the login done most recently. If the user has
         * not logged in before the first date will be returned.
         **/
        public DateTime LastLogin(string username)
        {
            var id = db.users.Single(u => u.username == username).id;
            int amount = db.lastvisits.Where(l => l.userid == id).Count();
            int skip = 0;
            if (amount > 1)
            {
                skip = 1;
            }
            DateTime date = db.lastvisits.Where(l => l.userid == id).OrderByDescending(l => l.visit).Skip(skip).Take(1).Single().visit;
            return date;
        }

        /**
         * Method to get the number of messages the user has not read, that has the readstatus set to 0.
         * Returns the number of unread messages as an int.
         **/
        public int UnreadMessages(string username)
        {
            var id = db.users.Single(u => u.username == username).id;
            int unread = db.messagetoes.Where(r => r.userid == id).Where(r => r.readstatus == 0).Count();
            return unread;
        }

        /**
         * Method to get all the message items in the inbox for a specified username. The messages are put in a
         * message wrapper class to display the data in an easier way. The users who are allowed to read the message
         * are put in the toUsers field. The list is then returned.
         **/
        public List<MessageWrapper> GetInbox(string username)
        {
            int id = db.users.Single(u => u.username == username).id;
            var messages = db.messagetoes.Where(u => u.userid == id);
            List<MessageWrapper> mwList = new List<MessageWrapper>();
            foreach (var m in messages)
            {
                MessageWrapper mw = new MessageWrapper();
                message message = db.messages.Single(r => r.id == m.messageid);
                user user = db.users.Single(u => u.id == message.fromUser);
                mw.messageId = message.id;
                mw.fromUser = user.username;
                mw.title = message.title;
                mw.posted = message.posted;
                if (m.readstatus == 1)
                {
                    mw.read = true;
                }
                if (m.readstatus < 2)
                {
                    var toUsers = db.messagetoes.Where(r => r.messageid == message.id);

                    List<string> temp = new List<string>();
                    foreach (var to in toUsers)
                    {
                        temp.Add(to.user.username);
                    }
                    mw.toUsers = temp;
                    mwList.Add(mw);
                }
            }
            return mwList;
        }

        /**
         * Method to inspect a specified message by it's id. The message is put in a message wrapper class to make the
         * displaying of data easier. The users who have received the message are added in the toUsers field.
         * The message is then returned.
         **/
        public MessageWrapper InspectMessage(int messageId)
        {
            MessageWrapper mw = new MessageWrapper();
            var message = db.messages.Single(m => m.id == messageId);
            mw.fromUser = db.users.Single(u => u.id == message.fromUser).username;
            mw.posted = message.posted;
            mw.title = message.title;
            mw.content = message.content;

            var messages = db.messagetoes.Where(m => m.messageid == messageId);

            List<string> temp = new List<string>();
            foreach (var m in messages)
            {
                temp.Add(m.user.username);
            }
            mw.toUsers = temp;
            return mw;
        }

        /**
         * Method to set the readstatus of a message. The message is fetched with a messageId and to who the message is.
         **/
        public void SetReadStatus(int status, int messageId, string username)
        {
            var userId = db.users.Single(u => u.username == username).id;
            messageto messagetoe = db.messagetoes.Where(m => m.messageid == messageId).Single(u => u.userid == userId);
            messagetoe.readstatus = status;
            db.Entry(messagetoe).State = System.Data.EntityState.Modified;
            db.SaveChanges();
        }

        /**
         * Method to delete a message. The message is never deleted in the database, but the readstatus is set to 2 so
         * the users won't be able to see it.
         **/
        public void DeleteMessage(int messageId, string username)
        {
            var user = db.users.Single(u => u.username == username);
            messageto message = db.messagetoes.Where(m => m.messageid == messageId).Single(z => z.userid == user.id);
            message.readstatus = 2;
            db.Entry(user).State = System.Data.EntityState.Modified;
            db.SaveChanges();
        }

        /**
         * Method to get all the userIds in a specified group name. A list with ints are created containing all the
         * userIds which are fetched from the database using the specified group name. The current users in the groups
         * are added to the list which are then returned.
         **/
        public List<int> GetUserIdsInGroup(List<string> groupNames)
        {
            List<int> groupIds = new List<int>();
            List<int> userIds = new List<int>();
            foreach (var g in groupNames)
            {
                groupIds.Add(db.groups.Single(s => s.name == g.Trim()).id);
            }

            List<usersingroup> ingroup = new List<usersingroup>();
            foreach (var g in groupIds)
            {
                List<int> test = db.usersingroups.Include("users").Where(m => m.groupid == g).Select(u => u.userid).ToList();
                userIds.AddRange(test.ToArray());
            }

            return userIds;
        }

        /**
         * Method to get the userIds by a list of usernames.
         * Returns a list of ints with userIds.
         **/
        public List<int> GetUserIds(List<string> userNames)
        {
            List<int> userIds = new List<int>();
            foreach (var u in userNames)
            {
                userIds.Add(db.users.Single(s => s.username == u.Trim()).id);
            }

            return userIds;
        }

        /**
         * Method to get a username by id which is then returned.
         **/
        public string getUserNameFromId(int id)
        {
            string username = null;
            try
            {
               username = db.users.Single(u => u.id == id).username;
            }
            catch { }
            return username;
        }

        /**
         * Method to create a message with a message wrapper and a username to who the message is from.
         * The new message is added to the database with the data from the message wrapper and the messageId
         * is then returned.
         **/
        public int CreateMessage(NewMessageWrapper newMessageWrapper, string username)
        {
            message message = new message();
            var id = db.users.Single(u => u.username == username).id;
            message.fromUser = id;
            message.posted = System.DateTime.Now;
            message.title = newMessageWrapper.title;
            message.content = newMessageWrapper.content;

            db.messages.Add(message);
            db.SaveChanges();

            return message.id;
        }

        /**
         * Method to send a message with a specified id to a list of userIds. To make sure the message isn't sent to a
         * user multiple times the userIds list will be distinct. The userIds are then added to the Message To table in the
         * database.
         **/
        public void SendMessageToUser(int messageId, List<int> userIds)
        {
            userIds = userIds.Distinct().ToList();
            foreach (var addUser in userIds)
            {
                db.messagetoes.Add(new messageto { messageid = messageId, userid = addUser, readstatus = 0 });
            }
            db.SaveChanges();
        }

        /**
         * Method for the WebService to get all the information that is requested. If some fields are not filled out
         * default data will be filled in. If everything goes well a message wrapper is created from the information
         * gathered from the database and is then returned.
         **/
        public List<MessageWrapper> WebService(string fromDate, string toDate, string title, string content, string fromWho, string username)
        {
            List<message> list = new List<message>();
            fromDate = fromDate.Replace('/', '-');
            toDate = toDate.Replace('/', '-');
            DateTime from;
            DateTime to;
            try
            {
                from = DateTime.Parse(fromDate);
            }
            catch
            {
                from = DateTime.Today;
            }
            try
            {
                to = DateTime.Parse(toDate);
            }
            catch
            {
                to = DateTime.Today.AddDays(1);
            }

            if (DateTime.Compare(from, to) > 0)
            {
                DateTime temp = from;
                from = to;
                to = temp;
            }

            var userId = db.users.Single(u => u.username == username).id;
            int fromUserId = -1;
            try
            {
                fromUserId = db.users.Single(p => p.username == fromWho).id;
            }catch{}

            List<message> messages = db.messages.Where(d => d.posted >= from).Where(d => d.posted <= to).ToList();
            if (!string.IsNullOrEmpty(fromWho) && fromUserId > 0)
            {
                messages = messages.Where(m => m.fromUser == fromUserId).ToList();
            }
            if (!string.IsNullOrEmpty(title))
            {
                messages = messages.Where(m => m.title.ToLower().Contains(title.ToLower())).ToList();
            }
            if (!string.IsNullOrEmpty(content))
            {
                messages.Where(m => m.content.Contains(content));
            }

            foreach (var m in messages)
            {
                try
                {
                    db.messagetoes.Where(c => c.messageid == m.id).Single(c => c.userid == userId);
                    list.Add(m);
                }
                catch { }
            }

            List<MessageWrapper> mwList = new List<MessageWrapper>();
            foreach (var m in list)
            {
                MessageWrapper mw = new MessageWrapper();
                mw.content = m.content;
                mw.messageId = m.id;
                mw.posted = m.posted;
                mw.title = m.title;
                mw.fromUser = getUserNameFromId(m.fromUser);
                mwList.Add(mw);
            }

            return mwList;
        }
    }
}